﻿/// <summary>
/// SURGE FRAMEWORK
/// Author: Bob Berkebile
/// Email: bobb@pixelplacement.com
/// 
/// Forces a referenced particle system to flow along this spline. To optimize performance limit Max Particles to just what is necessary.  Particle speed along the path is determined by Start Lifetime. 
/// 
/// </summary>

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pixelplacement;

[ExecuteInEditMode]
[RequireComponent (typeof (Spline))]
public class SplineControlledParticleSystem : MonoBehaviour
{
	#region Public Variables
	public float startRadius;
	public float endRadius;
	#endregion

	#region Private Variables
	[SerializeField] ParticleSystem _particleSystem;
	Pixelplacement.Spline _spline;
	ParticleSystem.Particle[] _particles;
	const float _prviousDiff = .01f;
	#endregion

	#region Init
	void Awake ()
	{
		_spline = GetComponent<Pixelplacement.Spline> ();
	}
	#endregion

	#region Loops
	void LateUpdate ()
	{
		if (_particleSystem == null) return;

		if (_particles == null) _particles = new ParticleSystem.Particle[_particleSystem.main.maxParticles];

		int aliveParticlesCount = _particleSystem.GetParticles (_particles);

		for (int i = 0; i < aliveParticlesCount; i++)
		{
			//get calculation pieces:
			float seedMax = Mathf.Pow (10, _particles[i].randomSeed.ToString ().Length);
			float seedAsPercent = _particles[i].randomSeed / seedMax;
			float travelPercentage = 1 - (_particles [i].remainingLifetime / _particles [i].startLifetime);

			//bypass issue while running at edit time when particles haven't reached the spline end:
			if (_spline.GetDirection (travelPercentage) == Vector3.zero) continue;

			//get a direction off our current point on the path - rotating by 1080 results in better distribution since Unity's randomSeed for particles favors lower numbers:
			Vector3 offshootDirection = Quaternion.AngleAxis (1080 * seedAsPercent, -_spline.GetDirection (travelPercentage)) * _spline.Up (travelPercentage);
			Vector3 previousOffshootDirection = Quaternion.AngleAxis (1080 * seedAsPercent, -_spline.GetDirection (travelPercentage - _prviousDiff)) * _spline.Up (travelPercentage - _prviousDiff);

			//cache our positions:
			Vector3 position = _spline.GetPosition (travelPercentage);

			//cache a previous position for velocity if possible:
			Vector3 lastPosition = position;
			if (travelPercentage - .01f >= 0) lastPosition = _spline.GetPosition (travelPercentage - _prviousDiff);
		
			//decide how far to offshoot from the spline based on where we are between the start and end radius:
			float offset = Mathf.Lerp (startRadius, endRadius, travelPercentage);
			float previousOffset = Mathf.Lerp (startRadius, endRadius, travelPercentage - _prviousDiff);

			//place particles depending on simulation space:
			Vector3 currentPosition = Vector3.zero;
			Vector3 previousPosition = Vector3.zero;

			switch (_particleSystem.main.simulationSpace)
			{
			case ParticleSystemSimulationSpace.Local:
				
				currentPosition = _particleSystem.transform.InverseTransformPoint (position + offshootDirection * offset);
				previousPosition = _particleSystem.transform.InverseTransformPoint (lastPosition + previousOffshootDirection * previousOffset);
				break;

			case ParticleSystemSimulationSpace.World:
			case ParticleSystemSimulationSpace.Custom:
				currentPosition = position + offshootDirection * offset;
				previousPosition = position + previousOffshootDirection * previousOffset;
				break;
			}

			//apply:
			_particles [i].position = currentPosition;
			_particles [i].velocity = currentPosition - previousPosition;
		}

		//apply the particle changes back to the system:
		_particleSystem.SetParticles (_particles, _particles.Length);
	}
	#endregion
}